/*
 * modbus_slave.c
 *
 *  Created on: 2022年2月16日
 *      Author: yjs
 */
#include "modbus_slave.h"
#include "modbus_lib.h"
#include "usart.h"
#include "log.h"
#include "string.h"
#include <rtthread.h>

extern struct rt_messagequeue static_mq2;

MODS_T g_tModS;
VARS_T g_tVarS;

void MODS_OutQueue(void)
{
    uint8_t buf[S_TX_BUF_SIZE];
    uint16_t cnt = 0;
    if(0 == rt_mq_recv(&static_mq2,&buf[0], sizeof(buf), 0)) {
        cnt = buf[0];
        cnt = (cnt<<8)| buf[1];
        usart_485trans(DTU_485, &buf[2], cnt);
    }
}
/***************************************************************/
void MODS_Ini(void) {
    memset(&g_tVarS, 0x00, sizeof(VARS_T));
    memset(&g_tModS, 0x00, sizeof(MODS_T));
    g_tModS.Slave_addr = 1;
    LOG_DEBUG("MODS_Ini! Default Addr is %d\r\n", g_tModS.Slave_addr);
}
void MODS_Deal(usart_cache* recv) {
#if 0
    LOG_DEBUG("ModbusSlave recv cnt[%03d] \r\n", recv->cnt - 3);
    for(uint8_t i = 3; i < recv->cnt; i++) {
        LOG_DEBUG("%x ", recv->data[i]);
    }
    LOG_DEBUG("\r\n");
#endif
    MODS_Poll(&recv->data[3], recv->cnt - 3);
}
void MODS_Poll(uint8_t *data, uint16_t cnt) {
//	HAL_UART_Transmit_DMA(&huart1, &data[0], cnt);

    memcpy(&g_tModS.RxBuf[0], &data[0], cnt);
    g_tModS.RxCount = cnt;

    uint16_t addr;
    uint16_t crc1;
    if (g_tModS.RxCount < 4) /* 接收到的数据小于4个字节就认为错误 */
    {
        LOG_ERROR("ModbusSlave Length Error\r\n");
        goto err_ret;
    }
    /* 计算CRC校验和 */
    crc1 = CRC16_Modbus(g_tModS.RxBuf, g_tModS.RxCount);
    if (crc1 != 0) {
        LOG_ERROR("ModbusSlave CRC Error\r\n");
        goto err_ret;
    }
    /* 站地址 (1字节） */
    addr = g_tModS.RxBuf[0]; /* 第1字节 站号 */
    if (addr != g_tModS.Slave_addr) /* 判断主机发送的命令地址是否符合 */
    {
        LOG_ERROR("ModbusSlave Addr Error\r\n");
        goto err_ret;
    }
    MODS_AnalyzeApp();
err_ret:
    NULL;
}

/*
 *********************************************************************************************************
 *	函 数 名: MODS_SendWithCRC
 *	功能说明: 发送一串数据, 自动追加2字节CRC
 *	形    参: _pBuf 数据；
 *			  _ucLen 数据长度（不带CRC）
 *	返 回 值: 无
 *********************************************************************************************************
 */
void MODS_SendWithCRC(uint8_t *_pBuf, uint8_t _ucLen) {
    uint16_t crc;
    uint8_t buf[S_TX_BUF_SIZE] = { 0x00 };
    memcpy(&buf[2], &_pBuf[0], _ucLen);
    crc = CRC16_Modbus(&buf[2], _ucLen);
    _ucLen += 2;
    buf[_ucLen++] = crc >> 8;
    buf[_ucLen++] = crc;
    _ucLen = _ucLen - 2;
    buf[0] = 0;
    buf[1] = _ucLen;
//    usart_485trans(DTU_485, &buf[2], _ucLen);
    rt_mq_send(&static_mq2, &buf[0], sizeof(buf));
}
/*
 *********************************************************************************************************
 *	函 数 名: MODS_SendAckOk
 *	功能说明: 发送正确的应答.
 *	形    参: 无
 *	返 回 值: 无
 *********************************************************************************************************
 */
void MODS_SendAckOk(void) {
    uint8_t txbuf[6];
    uint8_t i;

    for (i = 0; i < 6; i++) {
        txbuf[i] = g_tModS.RxBuf[i];
    }
    MODS_SendWithCRC(txbuf, 6);
}
/*
 *********************************************************************************************************
 *	函 数 名: MODS_SendAckErr
 *	功能说明: 发送错误应答
 *	形    参: _ucErrCode : 错误代码
 *	返 回 值: 无
 *********************************************************************************************************
 */
void MODS_SendAckErr(uint8_t _ucErrCode) {
    uint8_t txbuf[3];

    txbuf[0] = g_tModS.RxBuf[0]; /* 485地址 */
    txbuf[1] = g_tModS.RxBuf[1] | 0x80; /* 异常的功能码 */
    txbuf[2] = _ucErrCode; /* 错误代码(01,02,03,04) */

    switch(_ucErrCode) {
    case RSP_ERR_CMD:
        LOG_DEBUG("ModbusSlave Can't Support CMD", _ucErrCode);
        break;
    case RSP_ERR_REG_ADDR:
        LOG_DEBUG("ModbusSlave Reg Addr Is Error", _ucErrCode);
        break;
    case RSP_ERR_VALUE:
        LOG_DEBUG("ModbusSlave Reg DataArea Is Error ", _ucErrCode);
        break;
    case RSP_ERR_WRITE:
        break;
    }
    MODS_SendWithCRC(txbuf, 3);
}

/*
 *********************************************************************************************************
 *********************************************************************************************************
 */
uint8_t MODS_ReadCoilValue(uint8_t number) {
    return 0;
}
uint8_t MODS_WriteCoilValue(uint16_t reg, uint16_t value) {
    return 0;
}
uint8_t MODS_ReadDisValue(uint8_t number) {
    return 0;
}
uint8_t MODS_ReadRegValue(uint16_t reg_addr, uint8_t *reg_value) {
    return 0;
}
uint8_t MODS_WriteRegValue(uint16_t reg_addr, uint16_t reg_value) {
    return 0;
}
uint8_t MODS_ReadInputValue(uint16_t reg_addr, uint8_t *reg_value) {
    return 0;
}
/*
 *********************************************************************************************************
 *	函 数 名: MODS_AnalyzeApp
 *	功能说明: 分析应用层协议
 *	形    参: 无
 *	返 回 值: 无
 *********************************************************************************************************
 */
void MODS_AnalyzeApp(void) {
    switch (g_tModS.RxBuf[1]) /* 第2个字节 功能码 */
    {
    case 0x01: /* 读取线圈状态（此例程用led代替）*/
        MODS_01H();
        break;
    case 0x02: /* 读取输入状态（按键状态）*/
        MODS_02H();
        break;
    case 0x03: /* 读取保持寄存器（此例程存在g_tVar中）*/
        MODS_03H();
        break;
    case 0x04: /* 读取输入寄存器（ADC的值）*/
        MODS_04H();
        break;
    case 0x05: /* 强制单线圈（设置led）*/
        MODS_05H();
        break;
    case 0x06: /* 写单个保存寄存器（此例程改写g_tVar中的参数）*/
        MODS_06H();
        break;
    case 0x0F: /* 强制单线圈（设置led）*/
        MODS_0FH();
        break;
    case 0x10: /* 写多个保存寄存器（此例程存在g_tVar中的参数）*/
        MODS_10H();
        break;
    default:
        g_tModS.RspCode = RSP_ERR_CMD;
        MODS_SendAckErr(g_tModS.RspCode); /* 告诉主机命令错误 */
        break;
    }
}

/*
 *********************************************************************************************************
 *	函 数 名: MODS_01H
 *	功能说明: 读取线圈状态（对应远程开关D01/D02/D03）
 *	形    参: 无
 *	返 回 值: 无
 *********************************************************************************************************
 */
/* 说明:这里用LED代替继电器,便于观察现象 */
void MODS_01H(void) {
    /*
     举例：
     主机发送:
     11 从机地址
     01 功能码
     00 寄存器起始地址高字节
     13 寄存器起始地址低字节
     00 寄存器数量高字节
     25 寄存器数量低字节
     0E CRC校验高字节
     84 CRC校验低字节

     从机应答: 	1代表ON，0代表OFF。若返回的线圈数不为8的倍数，则在最后数据字节未尾使用0代替. BIT0对应第1个
     11 从机地址
     01 功能码
     05 返回字节数
     CD 数据1(线圈0013H-线圈001AH)
     6B 数据2(线圈001BH-线圈0022H)
     B2 数据3(线圈0023H-线圈002AH)
     0E 数据4(线圈0032H-线圈002BH)
     1B 数据5(线圈0037H-线圈0033H)
     45 CRC校验高字节
     E6 CRC校验低字节

     例子:
     01 01 10 01 00 03   29 0B	--- 查询D01开始的3个继电器状态
     01 01 10 03 00 01   09 0A   --- 查询D03继电器的状态
     */
    uint16_t reg;
    uint16_t num;
    uint16_t i;
    uint16_t m;
    uint8_t status[8];

    g_tModS.RspCode = RSP_OK;

    /* 没有外部继电器，直接应答错误 */
    if (g_tModS.RxCount != 8) {
        g_tModS.RspCode = RSP_ERR_VALUE; /* 数据值域错误 */
        return;
    }

    reg = BEBufToUint16(&g_tModS.RxBuf[2]); /* 寄存器号 */
    num = BEBufToUint16(&g_tModS.RxBuf[4]); /* 寄存器个数 */

    m = (num + 7) / 8;

    if ((reg >= REG_D01) && (num > 0) && (reg + num <= REG_DXX + 1)) {
        for (i = 0; i < m; i++) {
            status[i] = 0;
        }
        for (i = 0; i < num; i++) {
            if (MODS_ReadCoilValue(i + reg - REG_D01)) /* 读LED的状态，写入状态寄存器的每一位 */
            {
                status[i / 8] |= (1 << (i % 8));
            }
        }
    } else {
        g_tModS.RspCode = RSP_ERR_REG_ADDR; /* 寄存器地址错误 */
    }

    if (g_tModS.RspCode == RSP_OK) /* 正确应答 */
    {
        g_tModS.TxCount = 0;
        g_tModS.TxBuf[g_tModS.TxCount++] = g_tModS.RxBuf[0];
        g_tModS.TxBuf[g_tModS.TxCount++] = g_tModS.RxBuf[1];
        g_tModS.TxBuf[g_tModS.TxCount++] = m; /* 返回字节数 */

        for (i = 0; i < m; i++) {
            g_tModS.TxBuf[g_tModS.TxCount++] = status[i]; /* 继电器状态 */
        }
        MODS_SendWithCRC(g_tModS.TxBuf, g_tModS.TxCount);
    } else {
        MODS_SendAckErr(g_tModS.RspCode); /* 告诉主机命令错误 */
    }
}
/*
 *********************************************************************************************************
 *	函 数 名: MODS_02H
 *	功能说明: 读取输入状态（对应K01～K03）
 *	形    参: 无
 *	返 回 值: 无
 *********************************************************************************************************
 */
void MODS_02H(void) {
    /*
     主机发送:
     11 从机地址
     02 功能码
     00 寄存器地址高字节
     C4 寄存器地址低字节
     00 寄存器数量高字节
     16 寄存器数量低字节
     BA CRC校验高字节
     A9 CRC校验低字节

     从机应答:  响应各离散输入寄存器状态，分别对应数据区中的每位值，1 代表ON；0 代表OFF。
     第一个数据字节的LSB(最低字节)为查询的寻址地址，其他输入口按顺序在该字节中由低字节
     向高字节排列，直到填充满8位。下一个字节中的8个输入位也是从低字节到高字节排列。
     若返回的输入位数不是8的倍数，则在最后的数据字节中的剩余位至该字节的最高位使用0填充。
     11 从机地址
     02 功能码
     03 返回字节数
     AC 数据1(00C4H-00CBH)
     DB 数据2(00CCH-00D3H)
     35 数据3(00D4H-00D9H)
     20 CRC校验高字节
     18 CRC校验低字节

     例子:
     01 02 20 01 00 08  23CC  ---- 读取T01-08的状态
     01 02 20 04 00 02  B3CA  ---- 读取T04-05的状态
     01 02 20 01 00 12  A207   ---- 读 T01-18
     */

    uint16_t reg;
    uint16_t num;
    uint16_t i;
    uint16_t m;
    uint8_t status[8];

    g_tModS.RspCode = RSP_OK;

    if (g_tModS.RxCount != 8) {
        g_tModS.RspCode = RSP_ERR_VALUE; /* 数据值域错误 */
        return;
    }

    reg = BEBufToUint16(&g_tModS.RxBuf[2]); /* 寄存器号 */
    num = BEBufToUint16(&g_tModS.RxBuf[4]); /* 寄存器个数 */

    m = (num + 7) / 8;
    if ((reg >= REG_T01) && (num > 0) && (reg + num <= REG_TXX + 1)) {
        for (i = 0; i < m; i++) {
            status[i] = 0;
        }
        for (i = 0; i < num; i++) {
            if (MODS_ReadDisValue(reg - REG_T01 + i)) {
                status[i / 8] |= (1 << (i % 8));
            }
        }
    } else {
        g_tModS.RspCode = RSP_ERR_REG_ADDR; /* 寄存器地址错误 */
    }

    if (g_tModS.RspCode == RSP_OK) /* 正确应答 */
    {
        g_tModS.TxCount = 0;
        g_tModS.TxBuf[g_tModS.TxCount++] = g_tModS.RxBuf[0];
        g_tModS.TxBuf[g_tModS.TxCount++] = g_tModS.RxBuf[1];
        g_tModS.TxBuf[g_tModS.TxCount++] = m; /* 返回字节数 */

        for (i = 0; i < m; i++) {
            g_tModS.TxBuf[g_tModS.TxCount++] = status[i]; /* T01-02状态 */
        }
        MODS_SendWithCRC(g_tModS.TxBuf, g_tModS.TxCount);
    } else {
        MODS_SendAckErr(g_tModS.RspCode); /* 告诉主机命令错误 */
    }
}
/*
 *********************************************************************************************************
 *	函 数 名: MODS_03H
 *	功能说明: 读取保持寄存器 在一个或多个保持寄存器中取得当前的二进制值
 *	形    参: 无
 *	返 回 值: 无
 *********************************************************************************************************
 */
void MODS_03H(void) {
    /*
     从机地址为11H。保持寄存器的起始地址为006BH，结束地址为006DH。该次查询总共访问3个保持寄存器。
     主机发送:
     11 从机地址
     03 功能码
     00 寄存器地址高字节
     6B 寄存器地址低字节
     00 寄存器数量高字节
     03 寄存器数量低字节
     76 CRC高字节
     87 CRC低字节

     从机应答: 	保持寄存器的长度为2个字节。对于单个保持寄存器而言，寄存器高字节数据先被传输，
     低字节数据后被传输。保持寄存器之间，低地址寄存器先被传输，高地址寄存器后被传输。
     11 从机地址
     03 功能码
     06 字节数
     00 数据1高字节(006BH)
     6B 数据1低字节(006BH)
     00 数据2高字节(006CH)
     13 数据2 低字节(006CH)
     00 数据3高字节(006DH)
     00 数据3低字节(006DH)
     38 CRC高字节
     B9 CRC低字节
     */
    uint16_t reg;
    uint16_t num;
    uint16_t i;
    uint8_t reg_value[64];

    g_tModS.RspCode = RSP_OK;

    if (g_tModS.RxCount != 8) /* 03H命令必须是8个字节 */
    {
        g_tModS.RspCode = RSP_ERR_VALUE; /* 数据值域错误 */
        goto err_ret;
    }

    reg = BEBufToUint16(&g_tModS.RxBuf[2]); /* 寄存器号 */
    num = BEBufToUint16(&g_tModS.RxBuf[4]); /* 寄存器个数 */
    if (num > sizeof(reg_value) / 2) {
        return;
    }
    if ((reg >= REG_P01) && (num > 0)
            && (reg + num <= REG_PXX + 1)) {
        for (i = 0; i < num; i++) {
            if (MODS_ReadRegValue(reg, &reg_value[2 * i]) != 0) /* 读出寄存器值放入reg_value */
            {
                g_tModS.RspCode = RSP_ERR_REG_ADDR; /* 寄存器地址错误 */
                break;
            }
            reg++;
        }
    } else {
        g_tModS.RspCode = RSP_ERR_VALUE; /* 数据值域错误 */
        goto err_ret;
    }

err_ret:
    if (g_tModS.RspCode == RSP_OK) /* 正确应答 */
    {
        g_tModS.TxCount = 0;
        g_tModS.TxBuf[g_tModS.TxCount++] = g_tModS.RxBuf[0];
        g_tModS.TxBuf[g_tModS.TxCount++] = g_tModS.RxBuf[1];
        g_tModS.TxBuf[g_tModS.TxCount++] = num * 2; /* 返回字节数 */

        for (i = 0; i < num; i++) {
            g_tModS.TxBuf[g_tModS.TxCount++] = reg_value[2 * i];
            g_tModS.TxBuf[g_tModS.TxCount++] = reg_value[2 * i + 1];
        }
        MODS_SendWithCRC(g_tModS.TxBuf, g_tModS.TxCount); /* 发送正确应答 */
    } else {
        MODS_SendAckErr(g_tModS.RspCode); /* 发送错误应答 */
    }
}
/*
 *********************************************************************************************************
 *	函 数 名: MODS_04H
 *	功能说明: 读取输入寄存器（对应A01/A02） SMA
 *	形    参: 无
 *	返 回 值: 无
 *********************************************************************************************************
 */
void MODS_04H(void) {
    /*
     主机发送:
     11 从机地址
     04 功能码
     00 寄存器起始地址高字节
     08 寄存器起始地址低字节
     00 寄存器个数高字节
     02 寄存器个数低字节
     F2 CRC高字节
     99 CRC低字节

     从机应答:  输入寄存器长度为2个字节。对于单个输入寄存器而言，寄存器高字节数据先被传输，
     低字节数据后被传输。输入寄存器之间，低地址寄存器先被传输，高地址寄存器后被传输。
     11 从机地址
     04 功能码
     04 字节数
     00 数据1高字节(0008H)
     0A 数据1低字节(0008H)
     00 数据2高字节(0009H)
     0B 数据2低字节(0009H)
     8B CRC高字节
     80 CRC低字节
     */
    uint16_t reg;
    uint16_t num;
    uint16_t i;
    uint8_t input_value[64];

    g_tModS.RspCode = RSP_OK;

    if (g_tModS.RxCount != 8) /* 03H命令必须是8个字节 */
    {
        g_tModS.RspCode = RSP_ERR_VALUE; /* 数据值域错误 */
        goto err_ret;
    }

    reg = BEBufToUint16(&g_tModS.RxBuf[2]); /* 寄存器号 */
    num = BEBufToUint16(&g_tModS.RxBuf[4]); /* 寄存器个数 */
    if (num > sizeof(input_value) / 2) {
        return;
    }

    if ((reg >= REG_A01) && (num > 0) && (reg + num <= REG_AXX + 1)) {
        for (i = 0; i < num; i++) {
            if (MODS_ReadInputValue(reg, &input_value[2 * i]) != 0) /* 读出寄存器值放入reg_value */
            {
                g_tModS.RspCode = RSP_ERR_REG_ADDR; /* 寄存器地址错误 */
                break;
            }
            reg++;
        }
    } else {
        g_tModS.RspCode = RSP_ERR_VALUE; /* 数据值域错误 */
        goto err_ret;
    }

err_ret:
    if (g_tModS.RspCode == RSP_OK) /* 正确应答 */
    {
        g_tModS.TxCount = 0;
        g_tModS.TxBuf[g_tModS.TxCount++] = g_tModS.RxBuf[0];
        g_tModS.TxBuf[g_tModS.TxCount++] = g_tModS.RxBuf[1];
        g_tModS.TxBuf[g_tModS.TxCount++] = num * 2; /* 返回字节数 */

        for (i = 0; i < num; i++) {
            g_tModS.TxBuf[g_tModS.TxCount++] = input_value[2 * i];
            g_tModS.TxBuf[g_tModS.TxCount++] = input_value[2 * i + 1];
        }
        MODS_SendWithCRC(g_tModS.TxBuf, g_tModS.TxCount); /* 发送正确应答 */
    } else {
        MODS_SendAckErr(g_tModS.RspCode); /* 发送错误应答 */
    }
}

/*
 *********************************************************************************************************
 *	函 数 名: MODS_05H
 *	功能说明: 强制单线圈（对应D01/D02/D03）
 *	形    参: 无
 *	返 回 值: 无
 *********************************************************************************************************
 */
void MODS_05H(void) {
    /*
     主机发送: 写单个线圈寄存器。FF00H值请求线圈处于ON状态，0000H值请求线圈处于OFF状态
     。05H指令设置单个线圈的状态，15H指令可以设置多个线圈的状态。
     11 从机地址
     05 功能码
     00 寄存器地址高字节
     AC 寄存器地址低字节
     FF 数据1高字节
     00 数据2低字节
     4E CRC校验高字节
     8B CRC校验低字节

     从机应答:
     11 从机地址
     05 功能码
     00 寄存器地址高字节
     AC 寄存器地址低字节
     FF 寄存器1高字节
     00 寄存器1低字节
     4E CRC校验高字节
     8B CRC校验低字节
     */
    uint16_t reg;
    uint16_t value;

    g_tModS.RspCode = RSP_OK;

    if (g_tModS.RxCount != 8) {
        g_tModS.RspCode = RSP_ERR_VALUE; /* 数据值域错误 */
        goto err_ret;
    }

    reg = BEBufToUint16(&g_tModS.RxBuf[2]); /* 寄存器号 */
    value = BEBufToUint16(&g_tModS.RxBuf[4]); /* 数据 */

    if ((value != 0xff00) && (value != 0x0000)) {
        g_tModS.RspCode = RSP_ERR_VALUE; /* 数据值域错误 */
        goto err_ret;
    }

    (value == 0xff00) ? (value = 1) : (value = 0);

    if (0 != MODS_WriteCoilValue(reg, value)) {
        g_tModS.RspCode = RSP_ERR_REG_ADDR; /* 寄存器地址错误 */
    }

err_ret:
    if (g_tModS.RspCode == RSP_OK) /* 正确应答 */
    {
        MODS_SendAckOk();
    } else {
        MODS_SendAckErr(g_tModS.RspCode); /* 告诉主机命令错误 */
    }
}

/*
 *********************************************************************************************************
 *	函 数 名: MODS_06H
 *	功能说明: 写单个寄存器
 *	形    参: 无
 *	返 回 值: 无
 *********************************************************************************************************
 */
void MODS_06H(void) {
    /*
     写保持寄存器。注意06指令只能操作单个保持寄存器，16指令可以设置单个或多个保持寄存器
     主机发送:
     11 从机地址
     06 功能码
     00 寄存器地址高字节
     01 寄存器地址低字节
     00 数据1高字节
     01 数据1低字节
     9A CRC校验高字节
     9B CRC校验低字节
     从机响应:
     11 从机地址
     06 功能码
     00 寄存器地址高字节
     01 寄存器地址低字节
     00 数据1高字节
     01 数据1低字节
     1B CRC校验高字节
     5A	CRC校验低字节
     */
    uint16_t reg;
    uint16_t value;

    g_tModS.RspCode = RSP_OK;

    if (g_tModS.RxCount != 8) {
        g_tModS.RspCode = RSP_ERR_VALUE; /* 数据值域错误 */
        goto err_ret;
    }

    reg = BEBufToUint16(&g_tModS.RxBuf[2]); /* 寄存器号 */
    value = BEBufToUint16(&g_tModS.RxBuf[4]); /* 寄存器值 */

    if (MODS_WriteRegValue(reg, value) != 0) /* 该函数会把写入的值存入寄存器 */
    {
        g_tModS.RspCode = RSP_ERR_REG_ADDR; /* 寄存器地址错误 */
    }

err_ret:
    if (g_tModS.RspCode == RSP_OK) /* 正确应答 */
    {
        MODS_SendAckOk();
    } else {
        MODS_SendAckErr(g_tModS.RspCode); /* 告诉主机命令错误 */
    }
}

void MODS_0FH(void) {

    uint16_t reg_addr;
    uint16_t reg_num;
    uint8_t byte_num;
    uint8_t i;
    g_tModS.RspCode = RSP_OK;

    if (g_tModS.RxCount < 10) {
        g_tModS.RspCode = RSP_ERR_VALUE; /* 数据值域错误 */
        goto err_ret;
    }

    reg_addr = BEBufToUint16(&g_tModS.RxBuf[2]); /* 寄存器号 */
    reg_num = BEBufToUint16(&g_tModS.RxBuf[4]); /* 寄存器数量 */
    byte_num = g_tModS.RxBuf[6]; /* 后面的数据体字节数 */

    if (byte_num != (((reg_num - 1) / 8) + 1)) {
        g_tModS.RspCode = RSP_ERR_VALUE; /* 数据值域错误 */
        goto err_ret;
    }

    for (i = 0; i < reg_num; i++) {
        if (g_tModS.RxBuf[7 + i / 8] & 0x01) {
            if (0 != MODS_WriteCoilValue(reg_addr + i, 1)) {
                g_tModS.RspCode = RSP_ERR_REG_ADDR; /* 寄存器地址错误 */
            }
        } else {
            if (0 != MODS_WriteCoilValue(reg_addr + i, 0)) {
                g_tModS.RspCode = RSP_ERR_REG_ADDR; /* 寄存器地址错误 */
            }
        }
        g_tModS.RxBuf[7 + i / 8] >>= 1; //从低位开始
    }
err_ret:
    if (g_tModS.RspCode == RSP_OK) /* 正确应答 */
    {
        MODS_SendAckOk();
    } else {
        MODS_SendAckErr(g_tModS.RspCode); /* 告诉主机命令错误 */
    }
}

/*
 *********************************************************************************************************
 *	函 数 名: MODS_10H
 *	功能说明: 连续写多个寄存器.  进用于改写时钟
 *	形    参: 无
 *	返 回 值: 无
 *********************************************************************************************************
 */
void MODS_10H(void) {
    /*
     从机地址为11H。保持寄存器的其实地址为0001H，寄存器的结束地址为0002H。总共访问2个寄存器。
     保持寄存器0001H的内容为000AH，保持寄存器0002H的内容为0102H。

     主机发送:
     11 从机地址
     10 功能码
     00 寄存器起始地址高字节
     01 寄存器起始地址低字节
     00 寄存器数量高字节
     02 寄存器数量低字节
     04 字节数
     00 数据1高字节
     0A 数据1低字节
     01 数据2高字节
     02 数据2低字节
     C6 CRC校验高字节
     F0 CRC校验低字节

     从机响应:
     11 从机地址
     06 功能码
     00 寄存器地址高字节
     01 寄存器地址低字节
     00 数据1高字节
     01 数据1低字节
     1B CRC校验高字节
     5A	CRC校验低字节
     */
    uint16_t reg_addr;
    uint16_t reg_num;
    uint8_t byte_num;
    uint8_t i;
    uint16_t value;

    g_tModS.RspCode = RSP_OK;

    if (g_tModS.RxCount < 11) {
        g_tModS.RspCode = RSP_ERR_VALUE; /* 数据值域错误 */
        goto err_ret;
    }

    reg_addr = BEBufToUint16(&g_tModS.RxBuf[2]); /* 寄存器号 */
    reg_num = BEBufToUint16(&g_tModS.RxBuf[4]); /* 寄存器个数 */
    byte_num = g_tModS.RxBuf[6]; /* 后面的数据体字节数 */

    if (byte_num != 2 * reg_num) {
        g_tModS.RspCode = RSP_ERR_VALUE; /* 数据值域错误 */
        goto err_ret;
    }

    for (i = 0; i < reg_num; i++) {
        value = BEBufToUint16(&g_tModS.RxBuf[7 + 2 * i]); /* 寄存器值 */

        if (MODS_WriteRegValue(reg_addr + i, value) != 0) {
            g_tModS.RspCode = RSP_ERR_REG_ADDR; /* 寄存器地址错误 */
            break;
        }
    }

err_ret:
    if (g_tModS.RspCode == RSP_OK) /* 正确应答 */
    {
        MODS_SendAckOk();
    } else {
        MODS_SendAckErr(g_tModS.RspCode); /* 告诉主机命令错误 */
    }
}
